﻿#pragma once
#include "API.h"
#include <iostream>
#define CONNECT_TO_SERVER_WAIT_TIME		10000
#define SILENCE_CONNECTION_CHECK		60000
#define SILENCE_CONNECTION_DURATION		90000

#define DEF_HTTP_PORT	80
#define DEF_HTTPS_PORT	443
#define HEADER_SEP		"\r\n"
#define HTTPS_FLAG		"CONNECT"
#define CHUNK_FLAG		"chunked"
#define HTTP_SCHEME		"http://"
#define HTTPS_SCHEME	"https://"
#define HTTP_1_1		"HTTP/1.1"
#define HEAD_HOST		"Host"
#define HEAD_CONN		"Connection"
#define HEAD_PROXY_CONN	"Proxy-Connection"
#define HEAD_PROXY_AUTH	"Proxy-Authorization:"
#define HTTPS_RESP		"HTTP/1.1 200 Connection Established\r\nContent-Length: 0\r\n\r\n"
#define IE_OPT_DIALOG	"rundll32.exe shell32.dll, Control_RunDLL inetcpl.cpl, , 4"

static int HEADER_SEP_LEN = (int)strlen(HEADER_SEP);
static int HTTP_SCHEME_LEN = (int)strlen(HTTP_SCHEME);
static int HTTPS_SCHEME_LEN = (int)strlen(HTTPS_SCHEME);
static int HEAD_HOST_LEN = (int)strlen(HEAD_HOST);
static int HEAD_CONN_LEN = (int)strlen(HEAD_CONN);
static int HEAD_PROXY_CONN_LEN = (int)strlen(HEAD_PROXY_CONN);
static int HEAD_PROXY_AUTH_LEN = (int)strlen(HEAD_PROXY_AUTH);
static int HTTPS_RESP_LEN = (int)strlen(HTTPS_RESP);

typedef struct THttpContext
{
    volatile LONG	ref;
    volatile ENDPOINT_ID	serverConnID;
    volatile ENDPOINT_ID	agentConnID;

    BOOL		closed;
    BOOL		https;
    BOOL		chunk;
    std::string	host;
    USHORT		port;
    std::string	method;
    std::string	path;

    static THttpContext* Construct(ENDPOINT_ID svrConnID) { return new THttpContext(svrConnID); }
    static void Destruct(THttpContext* pObj) { delete pObj; }

    LONG IncRef() { return ::InterlockedIncrement(&ref); }

    LONG DecRef()
    {
        LONG val = ::InterlockedDecrement(&ref);
        if (val == 0)
            Destruct(this);

        return val;
    }

private:
    THttpContext(ENDPOINT_ID svrConnID)
        : ref(1L)
        , serverConnID(svrConnID)
        , agentConnID(0)
        , port(0)
        , closed(FALSE)
        , https(FALSE)
        , chunk(FALSE)
    {

    }

}* PTHttpContext;

class EventHandler : public TCPEventHandler
{
public:
    virtual void onTcpAccept(LPIOCPService pIOCPService, ENDPOINT_ID id, PTCP_CONN_INFO pConnInfo)
    {
        pIOCPService->setPrivate(id, THttpContext::Construct(id));
    }
    virtual void onTcpConnected(LPIOCPService pIOCPService, ENDPOINT_ID id, PTCP_CONN_INFO pConnInfo)
    {
        PTHttpContext phttp = (PTHttpContext)pIOCPService->getPrivate(id);
        if (!phttp) return;
        if (phttp->https)
            pIOCPService->sendMsg(phttp->serverConnID, HTTPS_RESP, HTTPS_RESP_LEN);
    }
    virtual void onTcpClosed(LPIOCPService pIOCPService, ENDPOINT_ID id)
    {
        PTHttpContext phttp = (PTHttpContext)pIOCPService->getPrivate(id);
        if(phttp)
        {
            phttp->closed = true;
            ENDPOINT_ID eid = (phttp->serverConnID == id) ? phttp->agentConnID : phttp->serverConnID;
            phttp->DecRef();
            pIOCPService->close(eid);
        }

    }
    virtual void onTcpReceive(LPIOCPService pIOCPService, ENDPOINT_ID id, LPWSABUF pWsaBuffer)
    {
        PTHttpContext phttp = (PTHttpContext)pIOCPService->getPrivate(id);
        DbgPrint("%s", pWsaBuffer->buf);
        if (!phttp) return;
        if ((phttp->serverConnID == id) && !phttp->chunk)
        {   
            std::string buffer(pWsaBuffer->buf, pWsaBuffer->len);
            auto npos = buffer.find("HTTP/");
            phttp->host = buffer.substr(0, npos);
            npos = phttp->host.find("CONNECT");
            phttp->port = DEF_HTTP_PORT;

            if (npos != std::string::npos)
            {
                phttp->method = HTTPS_FLAG;
                phttp->host = phttp->host.substr(npos + 8);
                npos = phttp->host.find("https://");
                if(npos != std::string::npos)
                {
                    phttp->port = DEF_HTTPS_PORT;
                    phttp->https = true;
                    phttp->host = phttp->host.substr(npos);
                }
                npos = phttp->host.find(":");
                if (npos != std::string::npos)
                {
                    phttp->port = std::atoi(phttp->host.substr(npos + 1).c_str());
                    phttp->host = phttp->host.substr(0, npos);
                }
                if (phttp->port == DEF_HTTPS_PORT)  phttp->https = true;
            }
            else
            {
                auto npos = buffer.find("GET");
                if (npos != std::string::npos)
                {
                   phttp->method = "GET";
                   phttp->host = buffer.substr(4);
                }
                else
                {
                    npos = buffer.find("POST");
                    if (npos == std::string::npos)
                    {
                        pIOCPService->close(id);
                        return;
                    }
                    phttp->method = "POST";
                    phttp->host = buffer.substr(5);
                }
                npos = phttp->host.find("http://");
                if (npos != std::string::npos)
                {
                    phttp->host = phttp->host.substr(npos + 7);
                }
                npos = phttp->host.find("/");
                if (npos != std::string::npos)
                {
                    phttp->host = phttp->host.substr(0, npos);
                }
                npos = phttp->host.find(":");
                if (npos != std::string::npos)
                {
                    phttp->port = std::atoi(phttp->host.substr(npos + 1).c_str());
                    phttp->host = phttp->host.substr(npos);
                }
            }
            struct hostent* remoteHost;
            remoteHost = gethostbyname(phttp->host.c_str());
            //phttp->host = remoteHost->h_name;
            if (!remoteHost) return;
            phttp->IncRef();
            phttp->agentConnID = pIOCPService->connect(inet_ntoa(*(in_addr*)*remoteHost->h_addr_list), phttp->port, AF_INET, phttp);
            if (phttp->agentConnID == INVALID_SOCKET)
            {
                phttp->DecRef();
                pIOCPService->close(id);
                return;
            }
            phttp->chunk = true;
            if (phttp->https) return;
        }

        ENDPOINT_ID eid = (phttp->serverConnID == id) ? phttp->agentConnID : phttp->serverConnID;
        pIOCPService->sendMsg(eid, pWsaBuffer->buf, pWsaBuffer->len);
    }
    virtual void onTcpCanSend(LPIOCPService pIOCPService, ENDPOINT_ID id)
    {
       
    }
};